/*
 * Created on 2004-11-17
 *
 * Copyright: Bluejay Networks Inc. & Inquiry Management Systems Ltd.  2004
 *
 * This software is the proprietary information of Bluejay Networks Inc. & 
 * Inquiry Management Systems Ltd.  
 * Use is subject to license terms.
 */
package com.ims.iaa.html;

import java.util.*;
import com.ims.iaa.AD.*;
import com.ims.iaa.Cache.IAppLvl;
import com.ims.iaa.IaaDebug;
import com.ims.iaa.IaaThread;
import com.ims.iaa.IaaUtil;
import com.ims.iaa.RobotsTXTAnalyzer;
import com.ims.iaa.SpiderDataStream;
/**
 * @author ShengMeng Bai
 *
 * For the sake of parallel process, Use ImpAnalyzerCtrl to control ImpAnalyzer,
 * and connect to the ad resouces.
 */
public class ImpAnalyzerCtrl{
	//save the free ImpAnalyzer
	LinkedList m_lstFree = new LinkedList();
	//save the ImpAnalyzer to wait for analyze the ad data
	LinkedList m_lstMission = new LinkedList();
	//save the ad recources connector.
	LinkedList m_lstConnector = new LinkedList();
	
	//this thread analyze the all impression's ad data
	Thread m_ImpAnalyzerThread = null;
	//the thread is looping for to analyze impression
	private static final int THREAD_CONTINUE = 1; 
	//the thread will be end, but it must wait for all ad data are analyzed
	private static final int THREAD_ENDING = 2; 
	//the thread is end.
	private static final int THREAD_END = 3; 
	int m_iThreadState = THREAD_END;
	
	long m_lLastActiveTime;
	
	private RobotsTXTAnalyzer m_robotsTXTAnz = null;
	SpiderDataStream m_sSpiderDataStream = null;
	
	private final static int WAITING_CONNECTOR = 200;
	private final static int WAITING_IMPRESSION = 500;
	private final static int CONNECTOR_COUNT = IaaDebug.m_iSpiderAuxThread;
	
	public ImpAnalyzerCtrl(){
		this(CONNECTOR_COUNT);
	}
	/**
	 * @param iCount, initial the ImpAnalyzer count.
	 */
	public ImpAnalyzerCtrl(int iCount){
		for(int i = 0; i < iCount; i++){
			m_lstFree.add(new ImpAnalyzerImpl());
			//++changsheng 2006-7-21
			((ImpAnalyzerImpl)m_lstFree.get(i)).setAnalyzerCtrl(this);
			//--
		}
		for(int i = 0; i < CONNECTOR_COUNT; i++){
			m_lstConnector.add(new AdDataConnector(i + 1));
		}
		m_lLastActiveTime = System.currentTimeMillis();
	}
	
	public long getLastActiveTime(){
		return m_lLastActiveTime;
	}
	/**
	 * Start analyze all impression.
	 * This thread run until all data of all impression are analyzed,
	 * you must call ImpressionOver() to notify it hasn't more impression,
	 * after this method are called, don't means this thread will end,
	 * it will wait all data of all impressioin are analyze, 
	 * you should call isMissionComplete() to check it.
	 * 
	 * @see ImpressionOver
	 * @see isMissionComplete
	 */
	public void startAnalyze(RobotsTXTAnalyzer robotsTXTAnz, SpiderDataStream sSpiderDataStream){
	    m_robotsTXTAnz = robotsTXTAnz;
	    m_sSpiderDataStream = sSpiderDataStream;
	    String sName = Thread.currentThread().getName();
		m_ImpAnalyzerThread = new IaaThread(sName){
			public void run(){
				try{
					while(m_iThreadState == THREAD_CONTINUE || m_lstMission.size() > 0){
						while(m_lstMission.size() > 0){  
							if(m_sSpiderDataStream.isReachMax()){
						        break ;
						    }
							//get the first element
							ImpAnalyzerImpl impAna = (ImpAnalyzerImpl)m_lstMission.get(0);
							//call parent's method to set the depth
							setDepth(impAna.getImpDepth());
							
							//++ Analyze all ad of this impression
							while(impAna.getAdCount() > 0 || !impAna.isAnalyzeOver()){
								if(impAna.getAdCount() > 0){
									AdDataLink adLink = impAna.getAdDataLink();
									//get the free connector
									AdDataConnector con = acquireConnector();
									while(con == null){
										try{ 
											sleep(WAITING_CONNECTOR);
										}catch(InterruptedException e){}
										con = acquireConnector();
									}
									//connect the data
									con.connect(adLink, impAna);
									m_lLastActiveTime = System.currentTimeMillis();
								}else{
									//waiting for the ad data.
									try{
										sleep(WAITING_IMPRESSION);
									}catch(InterruptedException e){}
								}//if(impAna.getAdCount() > 0)
							}//while(impAna.getAdCount() > 0 || !impAna.isAnalyzeOver()){
							//-- Analyze all ad of this impression
							
							//move the ImpAnalyzer to free ImpAnalyzer pool. 
							m_lstMission.remove(0);
							m_lstFree.add(impAna);
						}//while(m_lstMission.size() > 0)
					    if(m_sSpiderDataStream.isReachMax()){
					        break ;
					    }
						//++ waiting for ImpAnalyzer
						if(m_lstMission.size() == 0){
							try{
								sleep(WAITING_IMPRESSION);
							}catch(InterruptedException e){}
						}
						//-- waiting for ImpAnalyzer
					}
					//-- waiting for all connector
					boolean bWaiting = true;
					while(bWaiting){
						for(int i = 0; i < CONNECTOR_COUNT; i++){
							AdDataConnector con = (AdDataConnector)m_lstConnector.get(i);
							//if there connector is busy, waiting it, and break current loop.
							if(con.isConnecting()){
								try{
									sleep(WAITING_CONNECTOR);
									break;//break for(int i = 0;....
								}catch(InterruptedException e){}
							}
							if(!con.isStoped()){
								//stop connector thread.
								con.stopConnect();
							}
							//if all connectors are free, break while(true)
							if(i == CONNECTOR_COUNT - 1){
								bWaiting = false;//break while(true)
							}
						}
					}
				}catch (Exception e){
					IaaUtil.printStackTrace(e);
				}finally{
				    //remove all the mission impression,as may stop when reach max impression
				    while( m_lstMission.size() > 0 ){
						ImpAnalyzerImpl impAna = (ImpAnalyzerImpl)m_lstMission.get(0);
						impAna.resetAdList() ;
						m_lstMission.remove(0);
						m_lstFree.add(impAna);
				    }
				}
				//-- waiting for all connector
				m_iThreadState = THREAD_END;
			}//run
		};//m_ImpAnalyzerThread = new Thread(){
		m_iThreadState = THREAD_CONTINUE;
		//this thread start.
		m_ImpAnalyzerThread.start();
	}
	/**
	 * notify the thread, hasn't more impression to analyze.
	 *
	 */
	public synchronized void ImpressionOver(){
		m_iThreadState = THREAD_ENDING;
	}
	
	public boolean isMissionComplete(){
		return (m_iThreadState == THREAD_END);
	}
	/**
	 * register a free AdDataConnector object to the pool.
	 * @param con AdDataConnector object
	 */
	public AdDataConnector acquireConnector(){
		AdDataConnector con = null;
		synchronized(m_lstConnector){
			for(int i = 0; i < m_lstConnector.size(); i++){
				con = (AdDataConnector)m_lstConnector.get(i);
				if(!con.isConnecting()){
					break;
				}else{
					//if is timeout, stop the thread and reset the connector(it will create a new thread at next time start it).
					if(con.isTimeOut()){
						con.stopConnect();
                        break;
					}
					con = null;
				}
			}
		}
		return con;
	}
	
	/**
	 * Get a ImpAnalyzer object, then you can add the AdDataLink for analyze it.
	 * 
	 * @return the ImpAnalyzer object
	 */
	public ImpAnalyzer getImpAnalyzer(){
		ImpAnalyzerImpl obj = null;
		//Get the ImpAnalyzer object form list or new a new object.
		if(m_lstFree.size() > 0){
			obj = (ImpAnalyzerImpl)m_lstFree.getFirst();
			synchronized(m_lstFree){
				m_lstFree.removeFirst();
			}
		}else{
			obj = new ImpAnalyzerImpl();
			//++changsheng 2006-7-20 fix a issue 
			//Note:three may be return a new ImpAnalyzerImpl !!!!!!!!!!!hoho
			obj.setAnalyzerCtrl(this);
			//--
		}
		//set the SpiderDataStream
		obj.setSpiderDataStream(m_sSpiderDataStream);
		//add it to busy list, so thread can deal with it.
		synchronized(m_lstMission){
			m_lstMission.add(obj);
		}
		//add by ShengMengBai at 2005-04-30 for robots.txt
		obj.setRobotsTxtAnaluzer(m_robotsTXTAnz);
		return obj;
	}
}
/**
 * This class connect to a ad data(such as image, flash or applet)
 * in a single thread at one time.
 */
class AdDataConnector{
	//if the the connector hasn't task, it will sllep SLEEP_TIME until interrupt
	private static final int SLEEP_TIME = 300000;//5 minutes
	//if the the time of connector connect to the resource larger than TIME_OUT, it is timeout.
	private static final int TIME_OUT = 360000;//6 minutes
    private LinkedList m_lstAgency;
    private LinkedList m_lstAdSize;
	private SpiderDataStream m_stream;
	//the conector thread
	private IaaThread m_conThread = null;
	//if m_bContinue=true, the thread will always run, and waiting for the task,
	//else the thread will stop, and next time start the thread will create a new thread
	private volatile boolean m_bContinue = false;
	//if the thread is connecting to the resouce, the m_bNewTask is true,
	//if the thread is waiting for task, the m_bNewTask is false.
	private volatile boolean m_bNewTask = false;
	//record the task start time or the thread last wake up time. 
	private volatile long m_lLastAvtive = System.currentTimeMillis();
	//the task
	AdDataLink m_ad = null;
	//the AdDataConnector index
	private int m_iIndex = -1;
	
	public AdDataConnector(int iIndex){
	    m_iIndex = iIndex;
	}
	private ImpAnalyzer imp ;
	public synchronized void connect(AdDataLink ad, ImpAnalyzerImpl impAna){
		m_ad = ad;
		m_stream = impAna.getSpiderDataStream();
		m_lstAgency = m_stream.getAgencyData();
		m_lstAdSize = m_stream.getAdSizeData();//added by yxy 2008-05-14, for intCriteria calculation
		startConnect(impAna.getImpDepth());
		m_ad.setDepth(impAna.getImpDepth());//changsheng for scrawler depth
		imp = impAna ;//++
	}
	/**
	 * If the connector connect to the resource morethan IaaThread.LONG_SLEEP, will return true, else false.
	 * @return
	 */
	public synchronized boolean isTimeOut(){
		if(!m_bContinue || !m_bNewTask){
			return false;
		}
		long lInterval = System.currentTimeMillis() - m_lLastAvtive;
		if(lInterval > TIME_OUT)
		{
			IaaUtil.log("Aux Thead" + m_iIndex + ": TimeOut " + lInterval / 60000 + " mins.");
			return true;
		}
		return false;
	}
	/**
	 * @return true, if the connector is conenct the resource, else false.
	 */
	public synchronized boolean isConnecting(){
		return m_bNewTask;
	}
	/**
	 * @return true, if the connector thread is stoped.
	 */
	public synchronized boolean isStoped(){
		return !m_bContinue;
	}
	/**
	 * Stop current Reconect thread, 
	 * in some insatnce, the thread may be block too long time, so, you need stop it 
	 * @return, if stop successful, return true, else false. 
	 * 
	 * @see #isTimeOut()
	 */
	public synchronized boolean stopConnect(){
		try{
			m_bContinue = false;
			if(m_conThread != null){
				m_conThread.interrupt();
			}
			m_bNewTask = false;
			return true;
		}catch(Exception e){
			IaaUtil.printStackTrace(e);
			return false;
		}
	}

	private void startConnect(int iDepth){
		class MyThread extends IaaThread{
			public MyThread(String sName){
				super(sName);
			}
			
			public void run() {
				while(m_bContinue){
					m_lLastAvtive = System.currentTimeMillis();
					try{
						IaaDebug.begin("parse AdDataLink");
						//if the data need save, save it.
						int intDownloadLvl = -1 ;
						//0 no site and no app lvl
						//1 app lvl
						//--
						if(m_ad instanceof ImgLink  )
						{
							if(IaaDebug.SmartDownloadCounterMax != 0 )
							{
							    //++added by changsheng 2006-7-25
								intDownloadLvl = 1 ;
							    IaaUtil.auxLog("do app lvl");
								//should do appl lvl download
								int intFCR = m_stream.doFastCompare((ImgLink)m_ad) ;
								
								boolean failFlag = false ;
								if( intFCR == IAppLvl.FCR_FIRST){
								    if( ((ImgLink)m_ad ).parse1(m_lstAgency,m_lstAdSize) ){
								        intFCR =  m_stream.doPreCompare(((ImgLink)m_ad)) ;
								    }else{
									    m_stream.doCompare((ImgLink)m_ad,-1 );//set to not_match
									    failFlag = true ;
									}
								}
								if( !failFlag ){
									//wait for the compare result
								    int i = 0 ;
									while(  intFCR == IAppLvl.FCR_OVERLOAD ){
										try{Thread.sleep(2000); }catch(Exception ex ){}
										intFCR = m_stream.doFastCompare(((ImgLink)m_ad)) ;
										i ++ ;
										if( i > 10 )
										{
										    intFCR = IAppLvl.FCR_INITAL ;
										    break ;
										}
									}
									i=0;
									//wait for intRefImprID
									while(  intFCR == IAppLvl.FCR_WAIT ){
										try{Thread.sleep(2000); }catch(Exception ex ){}
										intFCR = m_stream.doFastCompare(((ImgLink)m_ad)) ;
										i ++ ;
										if( i > 10 )
										{
										    intFCR = IAppLvl.FCR_INITAL ;
										    break ;
										}
									}
									
									//do in doFastCompare
									if( intFCR == IAppLvl.FCR_MATCH ){
									    IaaUtil.auxLog("app lvl : match");
								    }else if( intFCR == IAppLvl.FCR_NOT_MATCH  ){
									    IaaUtil.auxLog("app lvl : not match");
										if(((ImgLink)m_ad ).parse(m_lstAgency,m_lstAdSize)){
											m_stream.setAdDataLink(m_ad);
										}
									}else if( intFCR == IAppLvl.FCR_INITAL ){
									    IaaUtil.auxLog("app lvl : inital");
										if( ((ImgLink)m_ad ).parse(m_lstAgency,m_lstAdSize) ){
											int thisImpressionID = m_stream.setAdDataLink(m_ad);
											IaaUtil.auxLog("app lvl : thisImpressionID is " + thisImpressionID);
											m_stream.doCompare((ImgLink)m_ad,thisImpressionID );
										}else{
										    m_stream.doCompare((ImgLink)m_ad,-1 );//set to not_match
										}
									} 
								}
							}else{
							    intDownloadLvl = 0 ;
							}
						}
						else
						{
							intDownloadLvl = 0 ;
						}//if(m_ad instanceof ImgLink  )
						
						if(intDownloadLvl == 0  && m_ad.parse(m_lstAgency,m_lstAdSize)){
							IaaDebug.begin("store AdDataLink");	
							m_stream.setAdDataLink(m_ad);
							IaaDebug.end("store AdDataLink");	
						}
						
						IaaDebug.end("parse AdDataLink");
					}catch(Exception e){
						IaaUtil.printStackTrace(e);
					}
					//register this AdDataConnector.
					m_bNewTask = false;
					//m_ImpReconnector.releaseConnector(m_owner);
					//waiting for new task
					while(m_bContinue && !m_bNewTask){
						try{
							m_lLastAvtive = System.currentTimeMillis();
							sleep(IaaThread.LONG_SLEEP);
						}catch(Exception e){
							//IaaUtil.log("Reconnector is interrupted");
							//IaaUtil.printStackTrace(e);
						}
					}
				}
			}
			
		};
		//the thread isn't runing, new thread and run it
		m_bNewTask = true;
		//add by ShengMengBai at 2005-05-26, we must set the active time,
		//else maybe timeout error. 
		m_lLastAvtive = System.currentTimeMillis();
		
		if(m_bContinue == false){
			m_bContinue = true;
			m_conThread = new MyThread(Thread.currentThread().getName() + "_Aux" + m_iIndex);
			m_conThread.setDepth(iDepth);
			m_conThread.start();
		}else{//thread is sleep, wake it up.
			try{
				m_conThread.setDepth(iDepth);
				m_conThread.interrupt();
			}catch(Exception e){
				//IaaUtil.printStackTrace(e);
			}
		}
	}
}
